const isObject = obj => typeof obj === 'object' && obj !== null 
const isFunction = func => typeof func === 'function' && func !== null
const isPointer = ptr => isObject(ptr) && isFunction(ptr)

function deepClone(obj,map = new WeakMap()){
    //easy case
    if(map.has(obj)) return map.get(obj);
    if([Set,Map,WeakSet,WeakMap,Date,RegExp].includes(obj.constructor)) return new obj.constructor(obj);

    //shallow
    let re = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
    
    //symbol
    for(let s of Object.getOwnPropertySymbols(obj))
        re[s] = isPointer(obj[s]) ? deepClone(obj[s], map) : obj[s]

    //cycle
    map.set(obj,re);

    //reflect
    for(let k of Reflect.ownKeys(obj))
        re[k] = isObject(obj[k]) ? deepClone(obj[k], map) : obj[k]

    return re;
};